package es.unex.meigas.gvsig.core;

import java.awt.geom.Rectangle2D;
import java.io.File;
import java.sql.Types;

import org.cresques.cts.IProjection;

import com.hardcode.gdbms.engine.data.driver.DriverException;
import com.hardcode.gdbms.engine.values.Value;
import com.iver.cit.gvsig.fmap.core.DefaultFeature;
import com.iver.cit.gvsig.fmap.core.FShape;
import com.iver.cit.gvsig.fmap.core.IGeometry;
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
import com.iver.cit.gvsig.fmap.drivers.DXFLayerDefinition;
import com.iver.cit.gvsig.fmap.drivers.DriverIOException;
import com.iver.cit.gvsig.fmap.drivers.FieldDescription;
import com.iver.cit.gvsig.fmap.drivers.LayerDefinition;
import com.iver.cit.gvsig.fmap.drivers.SHPLayerDefinition;
import com.iver.cit.gvsig.fmap.edition.DefaultRowEdited;
import com.iver.cit.gvsig.fmap.edition.EditionException;
import com.iver.cit.gvsig.fmap.edition.IRowEdited;
import com.iver.cit.gvsig.fmap.edition.IWriter;
import com.iver.cit.gvsig.fmap.edition.writers.dxf.DxfFieldsMapping;
import com.iver.cit.gvsig.fmap.edition.writers.dxf.DxfWriter;
import com.iver.cit.gvsig.fmap.edition.writers.shp.ShpWriter;
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
import com.iver.cit.gvsig.fmap.layers.ReadableVectorial;
import com.iver.cit.gvsig.fmap.layers.VectorialFileAdapter;
import com.vividsolutions.jts.geom.Geometry;

import es.unex.meigas.dataObjects.AbstractVectorLayer;
import es.unex.meigas.dataObjects.IFeatureIterator;
import es.unex.meigas.dataObjects.IVectorLayer;

public class gvVectorLayer extends AbstractVectorLayer {

	private final int PRECISION = 5;

	private String m_sFilename;
	private IWriter m_Writer;
	private int m_iGeometry;
	private String m_sName;
	private IProjection m_Projection;

	private ReadableVectorial m_RV;

	public void create(String sName, String sFilename, int iShapeType,
			Class[] types, String[] sFields, Object crs) {

		int iTypes[];
		LayerDefinition tableDef;

		m_sName = sName;
		m_sFilename = sFilename;
		m_iGeometry = 0;
		m_Projection = (IProjection) crs;

		try {

			if (sFilename.toLowerCase().endsWith("dxf")){
				m_Writer = new DxfWriter();
				((DxfWriter)m_Writer).setFile(new File(sFilename));
				((DxfWriter)m_Writer).setProjection((IProjection) crs);
				tableDef = new DXFLayerDefinition();
				tableDef.setShapeType(getgvSIGShapeType(iShapeType));

				DxfFieldsMapping fieldsMapping = new DxfFieldsMapping();
				((DxfWriter)m_Writer).setFieldMapping(fieldsMapping);

			}
			else{
				m_Writer = new ShpWriter();
				((ShpWriter)m_Writer).setFile(new File(sFilename));
				tableDef = new SHPLayerDefinition();
				tableDef.setShapeType(getgvSIGShapeType(iShapeType));
			}

			iTypes = DataTools.getgvSIGTypes(types);

			FieldDescription[] fields = new FieldDescription[sFields.length];
			for (int i = 0; i < fields.length; i++) {
				fields[i] = new FieldDescription();
				fields[i].setFieldName(sFields[i]);
				fields[i].setFieldType(iTypes[i]);
				fields[i].setFieldLength(getDataTypeLength(iTypes[i]));
				if (iTypes[i] == Types.DOUBLE){
					fields[i].setFieldDecimalCount(PRECISION);
				}
			}
			tableDef.setFieldsDesc(fields);
			tableDef.setName(sFilename);

			m_Writer.initialize(tableDef);
			m_Writer.preProcess();

			m_RV = null;


		} catch (Exception e){
			e.printStackTrace();
		}

	}

	public void create(FLyrVect layer) {

		m_BaseDataObject = layer;
		try{
			m_RV = layer.getSource();
			m_RV.start();
			m_Projection = layer.getProjection();
		}
		catch(Exception e){
			e.printStackTrace();
		}

	}

	public void open() {

		if (m_RV != null){
			try {
				m_RV.start();
			} catch (DriverIOException e) {
				e.printStackTrace();
			}
		};

	}

	public void close() {

		if (m_RV != null){
			try {
				m_RV.stop();
			} catch (DriverIOException e) {
				e.printStackTrace();
			}
		};

	}

	/**
     * Returns the length of field
     * @param dataType
     * @return length of field
     */
	public int getDataTypeLength(int dataType) {

		switch (dataType) {
		case Types.NUMERIC:
		case Types.DOUBLE:
		case Types.REAL:
		case Types.FLOAT:
		case Types.BIGINT:
		case Types.INTEGER:
		case Types.DECIMAL:
			return 20;
		case Types.CHAR:
		case Types.VARCHAR:
		case Types.LONGVARCHAR:
			return 254;
		case Types.DATE:
			return 8;
		case Types.BOOLEAN:
		case Types.BIT:
			return 1;
		}
		return 0;

	}

	public void addFeature(Geometry geom, Object[] values) {

		IGeometry iGeo = FConverter.jts_to_igeometry(geom);
		Value[] gvSIGValues = DataTools.getGVSIGValues(values);
		DefaultFeature feat = new DefaultFeature(iGeo, gvSIGValues, Integer.toString(m_iGeometry));
		IRowEdited editFeat = new DefaultRowEdited(feat, IRowEdited.STATUS_MODIFIED, m_iGeometry);
		m_iGeometry++;
		try {
			m_Writer.process(editFeat);
		} catch (Exception e) {
			e.printStackTrace();
		}


	}

	public IFeatureIterator iterator() {

		/*
		 * Layers being edited cannot be iterated...
		 */
		if (m_BaseDataObject instanceof FLyrVect){
			FLyrVect layer = (FLyrVect) m_BaseDataObject;
			gvFeatureIterator iter = new gvFeatureIterator(layer, m_Filters);
			return iter;
		}
		else{
			return null;
		}

	}

	public String getFieldName(int i) {

		if (m_RV != null){
			try {
				return m_RV.getRecordset().getFieldName(i);
			} catch (DriverException e) {
				e.printStackTrace();
				return null;
			}
		}

		return null; //TODO

	}

	public Class getFieldType(int i) {

		if (m_RV != null){
			try {
				return DataTools.getTypeClass(m_RV.getRecordset().getFieldType(i));
			} catch (DriverException e) {
				e.printStackTrace();
				return null;
			}
		}

		return null; //TODO

	}


	public int getFieldCount() {

		if (m_RV != null){
			try {
				return m_RV.getRecordset().getFieldCount();
			} catch (DriverException e) {
				e.printStackTrace();
				return 0;
			}
		}
		else if(m_Writer != null) {
			return m_Writer.getTableDefinition().getFieldsDesc().length;
		}

		return 0; //TODO

	}

	public int getShapesCount() {


		if (m_RV != null){
			try {
				int iCardinality = m_RV.getRecordset().getSelection().cardinality();
				if (iCardinality == 0){
					iCardinality = m_RV.getShapeCount();
				}
				return iCardinality;
			} catch (DriverIOException e) {
				e.printStackTrace();
				return 0;
			}
		}

		return 0; //TODO

	}

	public int getShapeType() {

		if (m_RV != null){
			try {
				return getShapeTypeFromGvSIGShapeType(m_RV.getShapeType());
			} catch (DriverIOException e) {
				e.printStackTrace();
				return IVectorLayer.SHAPE_TYPE_WRONG;
			}
		}

		return IVectorLayer.SHAPE_TYPE_WRONG;

	}

	private int getShapeTypeFromGvSIGShapeType(int shapeType) {

		switch (shapeType){
		case FShape.POLYGON:
			return IVectorLayer.SHAPE_TYPE_POLYGON;
		case FShape.LINE:
			return IVectorLayer.SHAPE_TYPE_LINE;
		case FShape.POINT:
			return IVectorLayer.SHAPE_TYPE_POINT;
		default:
			return IVectorLayer.SHAPE_TYPE_WRONG;
		}

	}

	private int getgvSIGShapeType(int shapeType) {

		switch (shapeType){
		case IVectorLayer.SHAPE_TYPE_POLYGON :
			return FShape.POLYGON;
		case IVectorLayer.SHAPE_TYPE_LINE:
			return FShape.LINE;
		case IVectorLayer.SHAPE_TYPE_POINT:
			return FShape.POINT;
		default:
			return FShape.POLYGON;
		}

	}

	public String getName() {

		if (m_BaseDataObject instanceof FLyrVect){
			FLyrVect layer = (FLyrVect) m_BaseDataObject;
			return layer.getName();
		}
		else{
			return m_sName;
		}

	}

	public void postProcess() {

		if (m_Writer == null){
			return;
		}

		try {
			m_Writer.postProcess();
			FLyrVect vectorLayer = (FLyrVect) FileTools.openLayer(
									m_sFilename, m_sName, m_Projection);
			create(vectorLayer);
		} catch (EditionException e) {
			e.printStackTrace();
		}

	}

	public Rectangle2D getFullExtent() {

		if (m_BaseDataObject instanceof FLyrVect){
			FLyrVect layer = (FLyrVect) m_BaseDataObject;
			try {
				return layer.getFullExtent();
			} catch (com.iver.cit.gvsig.fmap.DriverException e) {
				e.printStackTrace();
				return null;
			}
		}
		else{
			return null;
		}

	}

	public String getFilename() {

		if (m_BaseDataObject instanceof FLyrVect){
			FLyrVect layer = (FLyrVect) m_BaseDataObject;
			ReadableVectorial rv=((FLyrVect)layer).getSource();;
			if (rv instanceof VectorialFileAdapter) {
				return ((VectorialFileAdapter)rv).getFile().getAbsolutePath();
			}
			else{
				return null;
			}
		}
		else{
			return m_sFilename;
		}

	}

	public Object getCRS() {

		return m_Projection;

	}

	public void setName(String name) {

		m_sName = name;

	}

}
